﻿using Datav.Design.Core;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using Zdap.Common.Extensions;


namespace Datav.Design
{
    /// <summary>
    ///
    /// </summary>
    public static class TreeExtensions
    {
        /// <summary>
        /// 集合通用转为树形结构数据返回
        /// </summary>
        /// <example>
        /// 示例1：通用转为树形结构数据返回    
        /// <code>
        /// var tree = .ToTree(
        ///             idSelector: d => d.Id,
        ///              parentIdSelector: d => d.ParentId,
        ///              childrenSetter: (parent, children) => parent.Children = children
        ///          );
        /// </code>
        /// </example>

        public static IEnumerable<T> ToTree<T, TKey>(this IEnumerable<T> source, Func<T, TKey> idSelector, Func<T, TKey?> parentIdSelector, Action<T, IEnumerable<T>> childrenSetter) where TKey : struct
        {
            if (source == null) throw new ArgumentNullException(nameof(source));
            var items = source.ToList();
            if (!items.Any()) return items;
            // 构建查找表
            var lookup = items.ToLookup(parentIdSelector);
            // 获取根节点（处理null/0/default三种情况）
            var roots = lookup[default].Concat(GetZeroParentItems<T, TKey>(lookup)).Distinct().ToList();

            // 递归构建树
            foreach (var root in roots)
            {
                BuildTreeNodes(root, lookup, idSelector, childrenSetter);
            }
            return roots;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="TKey"></typeparam>
        /// <param name="lookup"></param>
        /// <returns></returns>
        private static IEnumerable<T> GetZeroParentItems<T, TKey>(ILookup<TKey?, T> lookup) where TKey : struct
        {
            try
            {
                // 显式指定类型参数转换
                var zeroKey = (TKey?)Convert.ChangeType(0, typeof(TKey));
                return lookup.Contains(zeroKey) ? lookup[zeroKey] : Enumerable.Empty<T>();
            }
            catch
            {
                return Enumerable.Empty<T>();
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="TKey"></typeparam>
        /// <param name="node"></param>
        /// <param name="lookup"></param>
        /// <param name="idSelector"></param>
        /// <param name="childrenSetter"></param>

        private static void BuildTreeNodes<T, TKey>(T node, ILookup<TKey?, T> lookup, Func<T, TKey> idSelector, Action<T, IEnumerable<T>> childrenSetter) where TKey : struct
        {
            var children = lookup[idSelector(node)].ToList();
            childrenSetter(node, children);
            foreach (var child in children)
            {
                BuildTreeNodes(child, lookup, idSelector, childrenSetter);
            }
        }
    }
}